package com.smallstep.apilimit.ratelimiter;

import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author 李俊
 * @Description  redis同分布同步锁
 * @Date 2019/9/4 14:16
 */
public class SyncLock {
    /**
     *  Redis key
     */
    private String key;

    private StringRedisTemplate stringRedisTemplate;
    /**
     *  expire Redis TTL/秒
     */
    private Long expire;
    /**
     * 安全时间/秒
     */
    private Long safetyTime;


    public SyncLock(String key, Long expire, Long safetyTime, StringRedisTemplate stringRedisTemplate) {
        this.key = key;
        this.expire = expire;
        this.safetyTime = safetyTime;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    private String value = getValue();

    private String getValue() {
        return Thread.currentThread().getId()+"-"+Thread.currentThread().getName();
    }
    /**
     * 尝试获取锁（立即返回）
     *
     * @return 是否获取成功
     */
    public boolean tryLock(){
        boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        if (locked) {
            stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return locked;
    }

    /**
     * 时隔多久进行
     */
    private long waitMillisPer  = 10;
    /**
     * 尝试获取锁，并至多等待timeout时长
     *
     * @param timeout 超时时长
     * @param unit 时间单位
     *
     * @return 是否获取成功
     */
    public boolean tryLock(Long timeout,TimeUnit unit){
        Long waitMax = unit.toMillis(timeout);
        Long waitAlready = 0L;
        while (stringRedisTemplate.opsForValue().setIfAbsent(key, value) != true && waitAlready < waitMax) {
            try {
                Thread.sleep(waitMillisPer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            waitAlready += waitMillisPer;
        }

        if (waitAlready < waitMax) {
            stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
            return true;
        }
        return false;
    }

    /**
     * 获取锁
     */
    public void lock() {
        Long waitMax = TimeUnit.SECONDS.toMillis(safetyTime);
        Long waitAlready = 0L;
        while (stringRedisTemplate.opsForValue().setIfAbsent(key, value) != true
                && waitAlready < waitMax) {
            try {
                Thread.sleep(waitMillisPer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            waitAlready += waitMillisPer;
        }
        stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }

    /**
     * 解锁
     */
    public void unLock() {
       String val = stringRedisTemplate.opsForValue().get(key);
       if (val.equals(value)) {
            stringRedisTemplate.delete(key);
       }
    }
}
